package gao.xiaolei.service;

import gao.xiaolei.copyDao.TestRequirementCopyDao;
import gao.xiaolei.dao.*;
import gao.xiaolei.entity.*;
import gao.xiaolei.exception.NoGradeRuleException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import gao.xiaolei.copyDao.TestDesignCopyDao;
import gao.xiaolei.copyDao.TestRunCopyDao;
import gao.xiaolei.copyEntity.TestDesignCopy;
import gao.xiaolei.copyEntity.TestReqCopy;
import gao.xiaolei.copyEntity.TestRunCopy;
import gao.xiaolei.util.ServiceName;
import gao.xiaolei.util.UuidUtil;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.util.LinkedList;
import java.util.List;

@Service("GradeRuleService")
public class GradeRuleService {

    @Resource(name = "GradeRuleDao")
    private GradeRuleDao gradeRuleDao;

    @Resource(name = "TestProjectDao")
    private TestProjectDao testProjectDao;

    @Autowired
    private RestTemplate restTemplate;

    @Resource(name = "TestRequirementCopyDao")
    private TestRequirementCopyDao testRequirementCopyDao;

    @Resource(name = "TestDesignCopyDao")
    private TestDesignCopyDao testDesignCopyDao;

    @Resource(name = "TestRunCopyDao")
    private TestRunCopyDao testRunCopyDao;

    @Resource(name = "TestPersonalGradeDao")
    private TestPersonalGradeDao testPersonalGradeDao;

    @Resource(name = "TimeSheetDao")
    private TimeSheetDao timeSheetDao;

    @Resource(name = "TestTeamGradeDao")
    private TestTeamGradeDao testTeamGradeDao;

    @Resource(name = "AnalysisService")
    private AnalysisService analysisService;

    @Resource(name = "TestBugDao")
    private TestBugDao testBugDao;

    @Transactional(readOnly = true)
    //获取某个测试项目的成绩
    public GradeRule getGradeRuleByProject(String projectId) {
        return gradeRuleDao.findByToProject(projectId);
    }

    @Transactional
    //给某个测试项目设置成绩结算规则
    public void addGradeRule(GradeRule gradeRule) {
        gradeRuleDao.save(gradeRule);
    }

    @Transactional
    //更改某个测试项目成绩结算规则
    public void updateGradeRule(GradeRule gradeRule) {
        testPersonalGradeDao.deleteAllByProject(gradeRule.getToProject());//如果更改了评分规则就要将之前评分规则打出来的分数全部清空
        testTeamGradeDao.deleteAllByProject(gradeRule.getToProject());
        gradeRuleDao.save(gradeRule);
    }

    @Transactional
    //给某个测试项目自动结算成绩
    public void automaticSettleGrade(String projectId) {
        GradeRule gradeRule = gradeRuleDao.findByToProject(projectId);
        if (gradeRule == null)
            throw new NoGradeRuleException("该项目没有设定评分规则！");
        String groupId = testProjectDao.getGroupId(gradeRule.getToProject()).orElseThrow(() -> new IllegalArgumentException("结算成绩的项目不属于任何组！"));
        testPersonalGradeDao.deleteAllByProject(projectId);//防止之前就已经生成过成绩
        testTeamGradeDao.deleteAllByProject(projectId);
        List<TestPersonalGrade> personalGradeList = new LinkedList<>();
        List<String> groupMember = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/groupMember/").concat(groupId), List.class);
        for (int i = 0, length = groupMember.size(); i < length; i++) {
            int personalGrade = 0;
            TestPersonalGrade testPersonalGrade = new TestPersonalGrade();
            testPersonalGrade.setId(UuidUtil.get32Uuid());
            testPersonalGrade.setUserId(groupMember.get(i));
            testPersonalGrade.setProject(projectId);
            if (gradeRule.getTrqIsInclude() == 1) {//如果成绩包括测试需求的话
                int trqGrade = 0;
                List<TestReqCopy> reqCopy = testRequirementCopyDao.findByAuthorAndProject(groupMember.get(i), projectId);//就将这个人的测试需求副本找出来
                if (reqCopy.size() == 0)//如果没有测试需求产出就给默认最低分
                    trqGrade = gradeRule.getTrqFloors();
                else {
                    for (int j = 0, length2 = reqCopy.size(); j < length2; j++) {//如果有测试需求产出就将测试需求的的分数累加起来
                        trqGrade = trqGrade + reqCopy.get(j).getGrade();
                    }
                    trqGrade = trqGrade / reqCopy.size();//最后测试需求部分的分数则是产出测试需求的平均分
                }
                testPersonalGrade.setTrgGrade(trqGrade);//记录测试需求的成绩
                personalGrade = personalGrade + trqGrade * gradeRule.getTrqPercent() / 100;//将测试需求的百分百分数加到个人总成绩
            }
            if (gradeRule.getDesignIsInclude() == 1) {//如果成绩包含测试计划的话
                int designGrade = 0;
                List<TestDesignCopy> designCopy = testDesignCopyDao.findByAuthorAndProject(groupMember.get(i), projectId);//就将这个人的测试计划副本找出来
                if (designCopy.size() == 0)
                    designGrade = gradeRule.getDesignFloors();
                else {
                    for (int j = 0, length2 = designCopy.size(); j < length2; j++) {//如果有测试计划产出就将测试计划的的分数累加起来
                        designGrade = designGrade + designCopy.get(j).getGrade();
                    }
                    designGrade = designGrade / designCopy.size();//最后测试计划部分的分数则是产出测试计划的平均分
                }
                testPersonalGrade.setDesignGrade(designGrade);//记录测试计划的成绩
                personalGrade = personalGrade + designGrade * gradeRule.getDesignPercent() / 100;//将测试计划的百分百分数加到个人总成绩
            }
            if (gradeRule.getRunIsInclude() == 1) {//如果成绩包含测试执行的话
                int runGrade = 0;
                List<TestRunCopy> runCopy = testRunCopyDao.findByAuthorAndProject(groupMember.get(i), projectId);//就将这个人的测试执行副本找出来
                if (runCopy.size() == 0)
                    runGrade = gradeRule.getRunFloors();
                else {
                    for (int j = 0, length2 = runCopy.size(); j < length2; j++) {//如果有测试执行产出就将测试执行的的分数累加起来
                        runGrade = runGrade + runCopy.get(j).getGrade();
                    }
                    runGrade = runGrade / runCopy.size();//最后测试执行部分的分数则是产出测试执行的平均分
                }
                testPersonalGrade.setRunGrade(runGrade);//记录测试执行的成绩
                personalGrade = personalGrade + runGrade * gradeRule.getRunPercent() / 100;//将测试执行的百分百分数加到个人总成绩
            }
            if (gradeRule.isBugIsInclude()) {//如果成绩包含测试bug的话
                int bugGrade = 0;
                List<TestBug> bugs = testBugDao.findAllByAuthorAndProjectId(groupMember.get(i), projectId);//就将这个人的测试Bug找出来
                if (bugs.size() == 0)
                    bugGrade = gradeRule.getRunFloors();
                else {
                    for (int j = 0, length2 = bugs.size(); j < length2; j++) {//如果有测试Bug产出就将测试BUg的的分数累加起来
                        bugGrade = bugGrade + bugs.get(j).getGrade();
                    }
                    bugGrade = bugGrade / bugs.size();//最后测试BUG部分的分数则是产出测试BUG的平均分
                }
                testPersonalGrade.setBugGrade(bugGrade);//记录测试Bug的成绩
                personalGrade = personalGrade + bugGrade * gradeRule.getBugPercent() / 100;//将测试Bug的百分百分数加到个人总成绩
            }
//            if (gradeRule.getReportIsInclude() == 1) {//如果成绩包含测试报告的话
//                int reportGrade = 0;
//                testPersonalGrade.setReportGrade(reportGrade);//记录测试报告的成绩
//                personalGrade = personalGrade + reportGrade * gradeRule.getReportPercent() / 100;//将测试报告的百分百分数加到个人总成绩
//            }
            if (gradeRule.getIsHasOtherGrade() == 1) {//如果成绩包含其他成绩的话
                testPersonalGrade.setOtherGrade(0);//其他成绩先给0分，真正的其他成绩在手动结算的时候给出
                personalGrade = personalGrade + 0 * gradeRule.getOtherGradePercent() / 100;//将其他分数的百分百分数加到个人总成绩
            }
            if (gradeRule.getIsCheckOnWork() == 1) {//如果成绩包含考勤成绩的话
                int checkGrade = 100;//考勤成绩
                int absenceFormDutyCount = 0;
                List<TimeSheet> timeSheets = timeSheetDao.findByProjectAndUserIdOrderByCheckTimeAsc(projectId, groupMember.get(i));//将此人在这个项目的所有打开记录找出来
                int timeQuantum = Period.between(gradeRule.getCheckBeginDate(), gradeRule.getCheckEndDate()).getDays() + 1;//获取评分制定的考勤时间段
                LocalDate thisCheckDate = gradeRule.getCheckBeginDate();
                boolean isAbsenceFormDuty;//判断是否缺勤
                for (int j = 0; j < timeQuantum; j++) {//以每一天为单位检查是否有在每一天的规定时间段打卡
                    isAbsenceFormDuty = true;
                    for (int k = 0, length2 = timeSheets.size(); k < length2; k++) {//遍历此人的考勤记录
                        if (thisCheckDate.isEqual(timeSheets.get(k).getCheckTime().toLocalDate())) {//如果在这一天有此人的考勤记录
                            LocalTime time = timeSheets.get(k).getCheckTime().toLocalTime();
                            if (time.isBefore(gradeRule.getCheckBeginTime()) || time.isAfter(gradeRule.getCheckEndTime()))//就看一下打卡的时间是否在规定的时间段内
                                isAbsenceFormDuty = false;
                        } else
                            isAbsenceFormDuty = false;
                    }
                    thisCheckDate = thisCheckDate.plusDays(1);//算出下一次循环检查的是哪一天
                    if (isAbsenceFormDuty) {//如果缺勤的话
                        checkGrade = checkGrade - gradeRule.getCheckMissOneDeduct();//考勤分数要减分
                        absenceFormDutyCount++;//缺勤次数加1
                    }
                }
                testPersonalGrade.setCheckGrade(checkGrade);
                testPersonalGrade.setAbsenceFormDutyCount(absenceFormDutyCount);//记录缺勤次数
                personalGrade = personalGrade + checkGrade * gradeRule.getCheckPercent() / 100;//将考勤的百分百分数加到个人总成绩
            }
            testPersonalGrade.setPersonalGrade(personalGrade);//保存个人总成绩（不包括团队成绩）
            personalGradeList.add(testPersonalGrade);
        }
        int teamGrade = 0;
        if (gradeRule.getIsCalculateTeamGrade() == 1) {//如果要计算团队成绩的话
            int averageGrade = 0;
            teamGrade = getTeamGrade(gradeRule, groupMember, personalGradeList, averageGrade, projectId);
            testTeamGradeDao.save(new TestTeamGrade(UuidUtil.get32Uuid(), projectId, groupId, teamGrade));//保存团队分数
        }
        for (TestPersonalGrade grade : personalGradeList) {//结算最终成绩
            if (gradeRule.getIsComposeWithTeamGrade() == 1) {//如果最终成绩包括团队成绩的话
                int personPercent = 100 - gradeRule.getTeamGradePercent();//计算个人单独成绩占最终成绩的比例
                grade.setTotal(grade.getPersonalGrade() * personPercent / 100 + teamGrade * gradeRule.getTeamGradePercent() / 100);
            } else//不然就直接把个人成绩作为最终成绩
                grade.setTotal(grade.getPersonalGrade());
            testPersonalGradeDao.save(grade);//将个人成绩保存到数据库
        }
    }

    @Transactional
    //给某个人手动结算成绩(即修改某个人的成绩)
    public void manualSettleGrade(TestPersonalGrade testPersonalGrade) {
        GradeRule gradeRule = gradeRuleDao.findByToProject(testPersonalGrade.getProject());
        String groupId = testProjectDao.getGroupId(gradeRule.getToProject()).orElseThrow(() -> new IllegalArgumentException("结算成绩的项目不属于任何组！"));
        List<String> groupMember = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/groupMember/").concat(groupId), List.class);
        for (int i = 0, length = groupMember.size(); i < length; i++) {
            if (groupMember.get(i).equals(testPersonalGrade.getUserId())) {
                groupMember.remove(i);
                break;
            }
        }
        int personalGrade = 0;
        TestPersonalGrade oldGrade = testPersonalGradeDao.findByUserIdAndProject(testPersonalGrade.getUserId(), testPersonalGrade.getProject());//获取旧的成绩
        if (gradeRule.getTrqIsInclude() == 1) {//如果成绩包括测试需求的话
            oldGrade.setTrgGrade(testPersonalGrade.getTrgGrade());//修改测试需求的成绩
            personalGrade = personalGrade + testPersonalGrade.getTrgGrade() * gradeRule.getTrqPercent() / 100;//将测试需求的百分百分数加到个人总成绩
        }
        if (gradeRule.getDesignIsInclude() == 1) {//如果成绩包含测试计划的话
            oldGrade.setDesignGrade(testPersonalGrade.getDesignGrade());//修改测试计划的成绩
            personalGrade = personalGrade + testPersonalGrade.getDesignGrade() * gradeRule.getDesignPercent() / 100;//将测试计划的百分百分数加到个人总成绩
        }
        if (gradeRule.getRunIsInclude() == 1) {//如果成绩包含测试执行的话
            oldGrade.setRunGrade(testPersonalGrade.getRunGrade());//修改测试执行的成绩
            personalGrade = personalGrade + testPersonalGrade.getRunGrade() * gradeRule.getRunPercent() / 100;//将测试执行的百分百分数加到个人总成绩
        }
        if (gradeRule.isBugIsInclude()) {//如果成绩包含测试Bug的话
            oldGrade.setBugGrade(testPersonalGrade.getBugGrade());//修改测试BUG的成绩
            personalGrade = personalGrade + testPersonalGrade.getBugGrade() * gradeRule.getBugPercent() / 100;//将测试bug的百分百分数加到个人总成绩
        }
//        if (gradeRule.getReportIsInclude() == 1) {//如果成绩包含测试报告的话
//            oldGrade.setReportGrade(testPersonalGrade.getReportGrade());//修改测试报告的成绩
//            personalGrade = personalGrade + testPersonalGrade.getReportGrade() * gradeRule.getReportPercent() / 100;//将测试报告的百分百分数加到个人总成绩
//        }
        if (gradeRule.getIsHasOtherGrade() == 1) {//如果成绩包含其他成绩的话
            oldGrade.setOtherGrade(testPersonalGrade.getOtherGrade());//其他成绩先给0分，真正的其他成绩在手动结算的时候给出
            personalGrade = personalGrade + testPersonalGrade.getOtherGrade() * gradeRule.getOtherGradePercent() / 100;//将其他分数的百分百分数加到个人总成绩
        }
        if (gradeRule.getIsCheckOnWork() == 1) {//如果成绩包含考勤成绩的话
            oldGrade.setCheckGrade(testPersonalGrade.getCheckGrade());
            personalGrade = personalGrade + testPersonalGrade.getCheckGrade() * gradeRule.getCheckPercent() / 100;//将考勤的百分百分数加到个人总成绩
        }
        oldGrade.setPersonalGrade(personalGrade);//保存个人总成绩（不包括团队成绩）
        int teamGrade = 0;
        List<TestPersonalGrade> teamGrades = null;
        if (gradeRule.getIsCalculateTeamGrade() == 1) {//如果要计算团队成绩的话
            int average = 0;
            teamGrades = testPersonalGradeDao.findAllByUserIdInAndProject(groupMember, testPersonalGrade.getProject());
            teamGrade = getTeamGrade(gradeRule, groupMember, teamGrades, average, gradeRule.getToProject());
            testTeamGradeDao.updateTeamGrade(testPersonalGrade.getProject(), groupId, teamGrade);//更新这个组在这个项目的平均分数
        }
        if (gradeRule.getIsComposeWithTeamGrade() == 1) {//如果最终成绩包括团队成绩的话,就要将组所有人的成绩都根据新的团队成绩再结算一次
            int personPercent = 100 - gradeRule.getTeamGradePercent();//计算个人单独成绩占最终成绩的比例
            oldGrade.setTotal(personalGrade * personPercent / 100 + teamGrade * gradeRule.getTeamGradePercent() / 100);//先将自己的个人总成绩算出来
            for (int i = 0, length = teamGrades.size(); i < length; i++) {//然后再去将组内其他人的个人最终成绩算出来
                teamGrades.get(i).setTotal(teamGrades.get(i).getPersonalGrade() * personPercent / 100 + teamGrade * gradeRule.getTeamGradePercent() / 100);
                testPersonalGradeDao.save(teamGrades.get(i));//保存到数据库
            }
        } else//不然就直接把个人成绩作为最终成绩
            oldGrade.setTotal(personalGrade);
        testPersonalGradeDao.save(oldGrade);//将个人成绩保存到数据库
    }

    private int getTeamGrade(GradeRule gradeRule, List<String> groupMember, List<TestPersonalGrade> teamGrades, int average, String toProject) {
        int teamGrade;
        for (TestPersonalGrade grade : teamGrades) {
            average = average + grade.getPersonalGrade();//先算出所有组员的分数总和
        }
        average = groupMember.size() == 0 ? 0 : average / groupMember.size();//将全组员的平均分作为团队分数
        float trqCoverPercent = analysisService.getProjectTrqCoverPercent(toProject);//获取项目的需求覆盖率
        int trqCoverGrade = (int) trqCoverPercent * gradeRule.getTrqCoverPercent() / 100;//算出项目需求覆盖率的分数
        teamGrade = average * gradeRule.getAveragePercent() / 100 + trqCoverGrade;//算出团队分数
        return teamGrade;
    }
}
